package com.sudoku.mode;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.sudoku.board.Board;
import com.sudoku.board.Box;
import com.sudoku.board.CorrectBoard;
import com.sudoku.board.Tile;
import com.sudoku.command.Command;
import com.sudoku.command.EnterNumberCommand;
import com.sudoku.command.EnterPencilNumberCommand;
import com.sudoku.command.EraseCommand;
import com.sudoku.command.RemovePencilCommand;
import com.sudoku.observer.Subject;
import com.sudoku.utility.Constants;
import com.sudoku.utility.Locator;

public abstract class Mode implements Constants {

	protected Board board;
	protected CorrectBoard correctBoard;
	protected Set<Tile> permanantTiles;
	protected Tile selectedTile;
	protected ArrayList<Tile> highlightedTiles;
	protected boolean debug = false;
	protected boolean pencilMode = false;
	protected Subject subject;
	protected int hintsUsed;

	public Mode(Subject subject) {
		this.subject = subject;
		hintsUsed = 0;
		permanantTiles = new HashSet<Tile>();
		highlightedTiles = new ArrayList<Tile>();
		board = new Board(subject);
		correctBoard = new CorrectBoard(subject);
	}

	public void init() {
		permanantTiles.clear();
		highlightedTiles.clear();
		board = new Board(subject);
		correctBoard = new CorrectBoard(subject);
	}

	public abstract void startGame();

	public abstract void render(SpriteBatch sb, BitmapFont font, BitmapFont pencilFont);

	public abstract void update(float delta);

	public Board getBoard() {
		return board;
	}

	public void setBoard(Board board) {
		this.board = board;
	}

	public CorrectBoard getCorrectBoard() {
		return correctBoard;
	}

	public void setCorrectBoard(CorrectBoard correctBoard) {
		this.correctBoard = correctBoard;
	}

	public Set<Tile> getPermanantTiles() {
		return permanantTiles;
	}

	public void setPermanantTiles(Set<Tile> permanantTiles) {
		this.permanantTiles = permanantTiles;
	}

	public void setSelectedTile(Tile tileSelected) {
		tileSelected.selected(pencilMode);
		selectedTile = tileSelected;
		// highlightedTiles.add(tileSelected);
		highlightSimilarTiles(tileSelected.getData());
	}

	public void highlightSimilarTiles(char c) {
		if (Locator.highlightTiles) {
			if (c != ' ') {
				for (Box b : board.getData()) {
					for (Tile t : b.getData()) {
						if (t.getData() == c) {
							t.highlight();
							highlightedTiles.add(t);
						}
					}
				}
			}
		}
	}

	public void unhighlightTiles() {
		for (Tile t : highlightedTiles) {
			t.unselect();
		}
		highlightedTiles.clear();
	}

	public void unselectTile(Tile tileSelected) {
		if (tileSelected != null) {
			tileSelected.unselect();
			selectedTile = null;
		}
		unhighlightTiles();

	}

	public abstract void solve();

	public void unhover(Tile tileHovered) {
		if (tileHovered != null) {
			tileHovered.unhover();
		}

	}

	public void hover(Tile tile) {
		tile.hover();
	}

	public Command setTileData(char character) {
		Command command = null;
		if (selectedTile != null && !permanantTiles.contains(selectedTile)) {
			command = new EnterNumberCommand(this, selectedTile, character);
		}
		return command;
	}

	public Command setPencilTileData(char character) {
		Command command = null;
		if (selectedTile != null && selectedTile.isBlank() && !permanantTiles.contains(selectedTile)) {
			//if tile already contains that pencil number, remove it
			if (selectedTile.getPencilNumbers().contains(Integer.valueOf(String.valueOf(character)))) {
				command = new RemovePencilCommand(selectedTile, character);
			} else {
				command = new EnterPencilNumberCommand(selectedTile, character);
			}
		}
		return command;
	}

	public void reset() {
		permanantTiles.clear();
		unhighlightTiles();
		unselectTile(selectedTile);
	}

	public void debug() {
		debug = !debug;

	}

	public boolean isPencilMode() {
		return pencilMode;
	}

	public void setPencilMode(boolean pencilMode) {
		this.pencilMode = pencilMode;
	}

	public void switchPencilMode() {
		pencilMode = !pencilMode;
		if (selectedTile != null) {
			selectedTile.selected(pencilMode);
		}

	}

	public Command erase() {
		if (selectedTile != null && !permanantTiles.contains(selectedTile)
				&& (selectedTile.getData() != ' ' || !selectedTile.getPencilNumbers().isEmpty())) {
			return new EraseCommand(this, selectedTile);
		}
		return null;
	}

	public void rehighlight() {
		if (selectedTile != null) {
			setSelectedTile(selectedTile);
		}

	}

	public Tile getSelectedTile() {
		return selectedTile;
	}

	public abstract void hint();

	public int getHintsUsed() {
		return hintsUsed;
	}

	public abstract void writeSave(Preferences prefs);

	public abstract void readSave(Preferences prefs);

	public abstract void solverDone();
}
